java.io.IOException: Too many open files问题

您所在的位置:网站首页 修改open files 不生效 java.io.IOException: Too many open files问题

java.io.IOException: Too many open files问题

2023-04-29 11:52| 来源: 网络整理| 查看: 265

在开发linux在线服务器的时候经常会遇会句柄泄露的问题。因为在linux系统设计里面遵循一切都是文件的原则,即磁盘文件、目录、网络套接字、磁盘、管道等,所有这些都是文件,在我们进行打开的时候会返回一个fd,即是文件句柄。如果频繁的打开文件,或者打开网络套接字而忘记释放就会有句柄泄露的现象。在linux系统中对进程可以调用的文件句柄数进行了限制,在默认情况下每个进程可以调用的最大句柄数是1024个,如果超过了这个限制,进程将无法获取新的句柄,而从导致不能打开新的文件或者网络套接字,对于线上服务器即会出现服务被拒绝的情况。

查看句柄:

在linux系统中可以通过ulimit–n查看每个进程限制的最大句柄数,通过ulimit –HSn 10240修改进程的最大句柄数。当句柄数目达到限制后,就回出现”too many files open”。

查看进程占用的句柄数有几种办法:

1) 通过cat/proc/pid/fd可以查看线程pid号打开的线程,cat   /proc/pid/fd |wc -l;

2) 通过lsof命令,需要root账号权限

查看当前系统的打开文件数:

# lsof | wc -l  

# watch "lsof | wc -l"

可以用lsof -p 看打开的文件句柄数.查看某个进程的打开文件数

#lsof -p  1234|wc -l

 

查看Linux系统打开的文件句柄数:

# lsof -n | awk '{print $2}'| sort | uniq -c | sort -nr 

   3009 31312     15 7364     14 8002     14 7777     14 7382     14 665     14 6308     ...第一列:进程打开的文件句柄数第二列:PID

 

应用日志报java.io.IOException: Too many open files

打开的文件过多,一般来说是由于应用程序对资源使用不当造成,比如没有及时关闭Socket或数据库连接等。但也可能应用确实需要打开比较多的文件句柄,而系统本身的设置限制了这一数量。

 

异常 1 java.net.SocketException: Too many open files

    at java.net.PlainSocketImpl.accept(Compiled Code)    at java.net.ServerSocket.implAccept(Compiled Code)    at java.net.ServerSocket.accept(Compiled Code)    at weblogic.t3.srvr.ListenThread.run(Compiled Code)

异常 2java.io.IOException:打开的文件过多

    at java.lang.UNIXProcess.forkAndExec(Native Method)    at java.lang.UNIXProcess.(UNIXProcess.java:54)    at java.lang.UNIXProcess.forkAndExec(Native Method)    at java.lang.UNIXProcess.(UNIXProcess.java:54)    at java.lang.Runtime.execInternal(Native Method)    at java.lang.Runtime.exec(Runtime.java:551)    at java.lang.Runtime.exec(Runtime.java:477)    at java.lang.Runtime.exec(Runtime.java:443)

...

第一个异常在错误影响到基础 TCP 协议时抛出,而第二个异常则在错误影响到 I/O 操作时抛出,这个是由于交换分区不足造成的。

文件打开数过多最坏的情况可以使系统崩溃,到时候只能是重起服务器了。 

 

原因:  

 1、大多数情况是程序没有正常关闭一些资源引起的,所以出现这种情况,请检查io读写,socket通讯等是否正常关闭。

  操作系统的中打开文件的最大句柄数受限所致,常常发生在很多个并发用户访问服务器的时候。因为为了执行每个用户的应用服务器都要加载很多文件(new一个socket就需要一个文件句柄),这就会导致打开文件的句柄的缺乏。 

       主要方向:在源码项目中查找所有引用了InputStream或者其他流的地方,查看其是否在使用完后,正常close掉,此处建议将流的close放到finally块中,这样异常时也会去close流。

       次要方向:session或者其他有用到socket的代码处,仔细查询看是否有没有释放资源的地方

    2、 如果检查程序没有问题,那就有可能是linux默认的open files值太小,不能满足当前程序默认值的要求,比如数据库连接池的个数,tomcat请求连接的个数等。。。 

         限制:有时是linux系统的参数的限制,需要修改内核参数。有时是中间件(weblogic、nginx)中启动文件参数限制,尤其是默认1024,在修改linux后若中间件有涉及也要同步修改。

      

 

解决:

尽量把类打成jar包,因为一个jar包只消耗一个文件句柄,如果不打包,一个类就消耗一个文件句柄.java的垃圾回收不能关闭网络连接打开的文件句柄,如果没有执行close()(例如:java.net.Socket.close())则文件句柄将一直存在,而不能被关闭.你也可以考虑设置socket的最大打开数来控制这个问题.对操作系统做相关的设置,增加最大文件句柄数量。

Linux在 Linux内核2.4.x中需要修改源代码,然后重新编译内核才生效。编辑Linux内核源代码中的 include/linux/fs.h文件,将 NR_FILE 由8192改为65536,将NR_RESERVED_FILES 由10 改为 128。编辑fs/inode.c 文件将MAX_INODE 由16384改为262144。或者编辑 /etc/sysctl.conf  文件增加两行 fs.file-max = 65536 和 fs.inode-max = 262144 。一般情况下,系统最大打开文件数比较合理的设置为每4M物理内存256,比如256M.

 

在linux下的fd是有限制的,一个是系统的限制,一个是用户进程限制。

 

服务器端修改:

查看系统允许打开的最大文件数

#cat /proc/sys/fs/file-max

sysctl fs.file-max=655360可以调整内核的阈值,前提得有root权限。可参考/etc/sysctl.conf,用man sysctl.conf命令sysctl -a可以显示所有的能够调整参数。

 

查看每个用户允许打开的最大文件数

ulimit -a

发现系统默认的是open files (-n) 1024,问题就出现在这里。

在系统文件/etc/security/limits.conf中修改这个数量限制,

在文件中加入内容:

* soft nofile 65536* hard nofile 65536

 

另外方法:1.使用ps -ef |grep java   (java代表你程序,查看你程序进程) 查看你的进程ID,记录ID号,假设进程ID为122.使用:lsof -p 12 | wc -l    查看当前进程id为12的 文件操作状况    执行该命令出现文件使用情况为 10523.使用命令:ulimit -a   查看每个用户允许打开的最大文件数    发现系统默认的是open files (-n) 1024,问题就出现在这里。4.然后执行:ulimit -n 4096     将open files (-n) 1024 设置成open files (-n) 4096

这样就增大了用户允许打开的最大文件数

 

同时查看ulimit实际是否生效,如果设置了但并未生效也不行。

运维实战案例之“Too many open files”错误与解决方法 

此文中就介绍了设置了参数,但并未生效引起的。

 

 

socket未关闭现象

1. 首先用lsof -p PID 查看一下打开文件的列表如果出现下图状况基本就两种可能,stream未关闭或者socket未关闭,出现can't identify protocol字样

2. 用netstat -anp | grep PID查看端口占用情况,若出现下图情况,证明Socket未关闭

原因:

  是因为Socket协议本身,若正确关闭一个Socket需要往返消息4次,若中途有未接到的消息就会停留在某个状态下,比如当前的CLOSE_WAIT。

        如果close_wait状态过多,可能是socket未关闭引起。

解决办法:

  客户端:主动关闭任何一个socket,尽量在finally中加入socket.close()语句。

  服务器:设置读取超时时间如:socket.setSoTimeout(3000);其次在用完之后主动关闭,做法同客户端方式。同时在任何的异常加入socket.shutdownInput();socket.shutdownOutput();

 

问题定位

问题定位步骤:1、   用root帐户 遍历 /proc/进程ID/fd目录,若该目录下文件数较大(如果大于10一般就属于socket泄漏),根据该进程ID,可以确认该进程ID所对应的名称。2、   重启程序恢复服务,以便后续查找问题。3、   strace 该程序并记录strace信息。strace –p 进程ID >>/tmp/stracelog.log 2>&14、   查看 /proc/进程ID/fd 下的文件数目是否有增加,如果发现有增加,记录上一个socket编号,停止strace5、   确认问题代码的位置。打开/tmp/stracelog.log,从尾部向上查找close(socket编号)所在行,可以确认在该次close后再次创建的socket没有关闭,根据socket连接的server ip可以确认问题代码的位置。

另一种方法:判断是否有socket泄漏:lsof | grep "can't identify protocol"如果存在很多,则代表socket泄漏,同时会显示哪个进程使用的sock未关闭。

 

文件句柄泄露定位方法:

1、使用lsof命令查出有哪些文件或者socket被打开。

2、根据文件名找到相应的模块,检查相关的代码.如果是socket,则根据端口号找到相应的模块,最后检查相关的代码。

 

 

 

 

mina高并发短连接导致java.io.IOException: Too many open files解决方案         //设置超时            connector.setConnectTimeoutMillis(defaultConnectTimeOut);           ConnectFuture connectFuture = connector.connect(address);           connectFuture.awaitUninterruptibly();   //同步,等待,直到连接完成            if  (connectFuture.isDone()) {               if  (!connectFuture.isConnected()) {  //若在指定时间内没连接成功,则抛出异常                    logger.info("fail to connect "  + logInfo);                      throw   new  Exception();               }           }  

 

         经过分析,导致主机文件句柄泄露的原因为,客户端发起服务端连接时,会请求系统分配相关的文件句柄,在原代码中,仅仅判断是否连接成功,而未对连接失败进 行资源释放,从而造成文件句柄泄露。当总的文件句柄数超过系统设置值(ulimit -n 查看同一个进程允许的最大文件句柄数),则抛出异常“java.io.IOException: Too many open files",导致无法创建新的连接,服务器挂掉。

      更改后的代码如下:

 

Java代码  收藏代码 final  NioSocketConnector connector =  new  NioSocketConnector();           final  String[] result =  new  String[ 1 ];           connector.getFilterChain().addLast("codec" ,                   new  ProtocolCodecFilter( new  ObjectSerializationCodecFactory()));           connector.setHandler(handler);              //设置超时            connector.setConnectTimeoutMillis(defaultConnectTimeOut);           ConnectFuture connectFuture = connector.connect(address);           connectFuture.awaitUninterruptibly();   //同步,等待,直到连接完成            if  (connectFuture.isDone()) {               if  (!connectFuture.isConnected()) {  //若在指定时间内没连接成功,则抛出异常                    logger.info("fail to connect "  + logInfo);                   connector.dispose();    //不关闭的话会运行一段时间后抛出,too many open files异常,导致无法连接                       throw   new  Exception();               }           }  

 

 

from http://www.jianshu.com/p/2e6748c4c0b7

 

原因一:Linux 的open files 数不够

[root@chances125 ~]# ulimit -a core file size (blocks, -c) 0 data seg size (kbytes, -d) unlimited scheduling priority (-e) 0 file size (blocks, -f) unlimited pending signals (-i) 78454 max locked memory (kbytes, -l) 64 max memory size (kbytes, -m) unlimited open files (-n) 1024 pipe size (512 bytes, -p) 8 POSIX message queues (bytes, -q) 819200 real-time priority (-r) 0 stack size (kbytes, -s) 10240 cpu time (seconds, -t) unlimited max user processes (-u) 1024 virtual memory (kbytes, -v) unlimited file locks (-x) unlimited lsof -n |awk '{print $2}'|sort|uniq -c |sort -nr|more #查一下当前已有的连接数,再来判断open files 为1024 是不是小 lsof -p $java_pid | wc -l #加大打开的文件数 ulimit -n 2048 #用户级别的修改 #系统级设置对所有用户有效。 #可通过两种方式查看系统最大文件限制 cat /proc/sys/fs/file-max #修改配置/etc/sysctl.conf文件 fs.file-max=2048 #通知系统启用这项配置 sysctl -p

 

原因二:TCP 参数配置不对

netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}' TIME_WAIT 7091 CLOSE_WAIT 2 ESTABLISHED 716 LISTEN 10

常用的三个状态是:ESTABLISHED 表示正在通信,TIME_WAIT 表示主动关闭,CLOSE_WAIT 表示被动关闭服务器保持了大量TIME_WAIT状态

#参数优化 #表示如果套接字由本端要求关闭,这个参数决定了它保持在FIN-WAIT-2状态的时间 net.ipv4.tcp_fin_timeout = 30 #表示当keepalive起用的时候,TCP发送keepalive消息的频度。缺省是2小时,改为300秒,原因是:当前都是图片,不需要建立长链接 net.ipv4.tcp_keepalive_time = 300 #表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭 net.ipv4.tcp_syncookies = 1 #表示如果套接字由本端要求关闭,这个参数决定了它保持在FIN-WAIT-2状态的时间 net.ipv4.tcp_tw_reuse = 1 #表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭,当前TIME-WAIT 过多,所以开启快速回收 net.ipv4.tcp_tw_recycle = 1 net.ipv4.ip_local_port_range = 5000 65000

配置生效后,观察结果,TIME_WAIT 明显减少。

TIME_WAIT 2492 CLOSE_WAIT 10 ESTABLISHED 730 LISTEN 10

原因三:Tomcat 配置不对

connectionTimeout - 网络连接超时,单位:毫秒。设置为0表示永不超时,这样设置有隐患的。通常可设置为30000毫秒。当前配置connectionTimeout=0

根据公式:服务器端最佳线程数量=((线程等待时间+线程cpu时间)/线程cpu时间) * cpu数量

待测试验证

 

原因四:程序打开了许多文件,但未关闭连接

之前一直在找程序哪里打开了文件却未关闭连接,未果。才想到连接打开后,多久可以关闭。

lsof -i :80 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME java 3011 root 837u IPv6 3513129 0t0 TCP sc-epg-app15:8085->10.223.52.16:38494 (ESTABLISHED) java 3011 root 838u IPv6 31731 0t0 TCP sc-epg-app15:8085->10.230.160.147:38903 (ESTABLISHED) java 3011 root 839u IPv6 3667505 0t0 TCP sc-epg-app15:8085->10.251.207.71:35411 (ESTABLISHED) #NODE 对应的是TCP #PID 3011,当前服务器上只有一个应用。

待解决的问题是:1)如何定位到程序代码什么地方打开了文件?2)打开的这些文件是不是都是必要的?3)can't identify protocol的原因

 

参考资料:

1、http://www.2cto.com/kf/201212/175863.html

2、http://marsvaadin.iteye.com/blog/1698924

3、MINA2 错误解决方法-- Linux下tomcat报错“java.net.SocketException: Too many open files”

4、tcp socket文件句柄泄漏

 



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3